import { Injectable } from '@angular/core';

import { BehaviorSubject, Observable } from 'rxjs';

import { User } from '../../users-demo/shared/user.model';
import { GeneralConfig } from '../config/general.config';
import { CommonService } from './common.service';

/**
 * Authentication service which provides Observable variable.
 */
@Injectable()
export class AuthenticationService {
    // Stores the URL so we can redirect after logging in
    redirectUrl: string;

    // Using BehaviorSubject instead of Subject because:
    // - BehaviorSubject has the default value.
    // - First time the observor subscribes can get the current value immediately. Subject triggers in the next round.
    // - If the observor stays in a Service's constructor, it matters, because the service will be constructed only 
    //      when it is needed while the Observable might already has value. The service does not have to wait until next subscription happens.
    private isAuthenticatedSubject: BehaviorSubject<boolean> = new BehaviorSubject(true);
    private isScreenLockedSubject: BehaviorSubject<boolean> = new BehaviorSubject(false);
    private currentUserSubject: BehaviorSubject<any> = new BehaviorSubject(null);

    constructor(private commonService: CommonService) {
        // Loads the user from session storage at the first time.
        let user = this.commonService.loadSessionData(GeneralConfig.localUserInfoStorageKey);

        // If user exists mean current has logged in.
        if (user) {
            this.isAuthenticatedSubject.next(true);
            this.currentUserSubject.next(user);
        }
    }

    get currentAuthenticationState(): boolean {
        return this.isAuthenticatedSubject.value;
    }

    get isAuthenticated$(): Observable<boolean> {
        return this.isAuthenticatedSubject.asObservable();
    }

    get currentScreenLockState(): boolean {
        return this.isScreenLockedSubject.value;
    }

    get isScreenLocked$(): Observable<boolean> {
        return this.isScreenLockedSubject.asObservable();
    }

    get currentUserState(): User {
        return this.currentUserSubject.value;
    }

    get currentUser$(): Observable<User> {
        return this.currentUserSubject.asObservable();
    }

    getAuthorizationHeader(): string {
        return '';
    }

    /**
     * Notifies the observer that screen has locked.
     */
    lockScreen(): void {
        this.isScreenLockedSubject.next(true);
    }

    /**
     * Notifies the observer that screen has unlocked.
     */
    unlockScreen(): void {
        this.isScreenLockedSubject.next(false);
    }

    /**
     * Notifies the observer that user has logged out.
     * @param user The user object that will be sent to all observers.
     */
    saveUserInfo(user: any): void {
        this.commonService.saveSessionData(GeneralConfig.localUserInfoStorageKey, user);
        this.isAuthenticatedSubject.next(true);
        this.currentUserSubject.next(user);
    }

    /**
     * Notifies the observer that user has logged out and clears the redirectUrl.
     */
    removeUserInfo(): void {
        this.commonService.removeSessionData(GeneralConfig.localUserInfoStorageKey);
        this.isAuthenticatedSubject.next(false);
        this.currentUserSubject.next(null);
        this.redirectUrl = null;
    }

    /**
     * Checks if the session data exists. Triggers isAuthenticated$ as true if exists.
     */
    checkSessionDataExisting(): void {
        if (this.commonService.loadSessionData(GeneralConfig.localUserInfoStorageKey)) {
            this.isAuthenticatedSubject.next(true);
        }
    }
}